package edu.cmu.arch.pipesandfilters;

import java.io.*;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import edu.cmu.arch.pipesandfilters.frame.*;

/**
 * The FileFilterSink writes to file the data that is 
 * received from its input streams and converts the bytes to a Frame
 * object. This facilitates getting column names and format the file without
 * doing any conversion of bytes in this class
 * 
 * @author Lorena Vazquez
 * 
 */
public class FileSinkFilter extends FrameFilterFramework {
	
	FileWriter outputFileWriter = null;
	File file = null;
	String delimiter = "\t";//tab delimited file by default
	Calendar TimeStamp = Calendar.getInstance();
	SimpleDateFormat TimeStampFormat = new SimpleDateFormat("yyyy:MM:dd:hh:mm:ss");
	int frameCnt = 0;
	ArrayList<Variable> columnList = new ArrayList<Variable>();//list of columns to be used for order of columns in file
	String separatorLine="";//line that will separate header from data - length defined by columns 
	/**  
	 * @param columns: columns and their order for output file
	 * @param outputFileName: the name of the output file 
	 * @throws IOException 
	 */
	public FileSinkFilter(ArrayList<Variable> columns,String outputFileName) throws IOException
	{
		try 
		{
			file = new File(outputFileName);
			//create directories if they don't exist
			if(file.getParentFile() != null)
				file.getParentFile().mkdirs();
			//open file for sink to write to
			outputFileWriter = new FileWriter(file);
			columnList = columns;
			String headerFormat;
			Variable column;
			int headerSize=0;
			//write header
			for (int i =0; i < columnList.size();i++)
			{
				column = columnList.get(i);
				if(column != Variable.ExtrapolatedId)
				{
					headerFormat= MessageFormat.format("%-{0}s",column.getMaxColumnWidth());
					outputFileWriter.write(String.format(headerFormat,column.getColumnName()));//write header	
					headerSize+=column.getMaxColumnWidth();
				}
			}
			for (int j=0;j<headerSize;j++)
			{
				separatorLine+="-";
			}
			outputFileWriter.write("\n");//write new line to finish header
			//write separatorLine
			outputFileWriter.write(separatorLine+"\n");
		} 
		catch (FileNotFoundException e) 
		{
			System.out.println(this.getName() + "::FileSinkFilter() FileNotFoundException occured; " + outputFileName + "not found" + e.getMessage());
		}
	}
	
	/**
	 * This is where the sink will process the bytes and convert to ASCII
	 * to write file.
	 * 1. The frame must be read and converted to a Frame object
	 * 2. Order the output the way that the outputFormat is described
	 * (non-Javadoc)
	 * @see edu.cmu.arch.pipesandfilters.FilterFramework#run()
	 */
	public void run()
    {		
		System.out.print( "\n" + this.getName() + "::FileSinkFilter Reading ");
		Frame frame = null;
		
		try
		{
			while (true)
			{
				
				frame = readFrame();//convert stream to Frame										
				for (int i =0; i < columnList.size();i++)
				{
					Variable v = columnList.get(i);
					Integer ExtrapolatedID = (Integer)frame.getValue(Variable.ExtrapolatedId);
					String format = MessageFormat.format("%-{0}s",v.getMaxColumnWidth());
					
					
					if(frame.getValue(v) != null)
					{
						if(v == Variable.Time && frame.getValue(Variable.Time) != null)
						{
							//Time variable has specific format
							TimeStamp.setTimeInMillis(frame.getValue(Variable.Time).longValue());
							outputFileWriter.write(String.format(format,TimeStampFormat.format(TimeStamp.getTime())));
							
						}
						else if(v != Variable.ExtrapolatedId)
						{
							//write the value formatted to 5 decimal places and pad out to 7 spaces
							 
							
							//write extrapolated field with asterisk
							if((ExtrapolatedID != null)&&(ExtrapolatedID == v.getId()))
							{
								outputFileWriter.write(String.format(format,String.format("%.5f*",frame.getValue(v))));
							}else
							{
								outputFileWriter.write(String.format(format,String.format("%.5f",frame.getValue(v))));
							}
						}
							
					}
				}												
			
			
			outputFileWriter.write("\n");//add new line after processing frame				
			
				
			}//while
		}
		catch (EndOfStreamException e)
		{
			
			CloseFilter();
			System.out.print( "\n" + this.getName() + "::EndOfStreamException::Sink Exiting;" );
		

		}
		catch (IOException e)
		{
			
			CloseFilter();
			System.out.print( "\n" + this.getName() + "::IOException::Sink Exiting;" );
		

		}// catch
			
	
    }
	
	

	/**
	 *  Finished processing,clean up any object 
	 *  and close files
	 */
    protected void CloseFilter()
    {
    	ClosePorts();//call closeports from FrameFilterFramework
    	CloseFiles();
		System.out.println(this.getName() + "::FileFilterSink Exiting;");
    }
    /**
     * Closes the output file
     */
    void CloseFiles()
    {
    	try
    	{    		
    		outputFileWriter.close();    		
    		System.out.println(this.getName() + "Closed All Files:FileFilterSink Exiting;");
    	}
    	catch(IOException e)
    	{
    		System.out.println(this.getName() + "::CloseFiles() IOException occured; Error closing output file" + e.getMessage());
    	}
    }
}
